/*
 * IUrlAccessor.cs
 * 
 * Microsoft Office SharePoint Server Managed Protocol Handler
 * 
 * Author: John Kozell (johnkoz@microsoft.com)
 *         Microsoft Coroporation
 * 
 * This code is an adaptation from, and interpretation of, the original SPS 2001 SDK
 * and has been updated with available information at the time of writing.
 * Not all the delcarions and alogrithms have been verified and tested
 * and there is no guarantee that they are accurate or valid, now or in the future.
 * 
 * The .NET Managed COM Interop approach contained herin was NOT created in cooperation
 * with the Microsoft SharePoint product group and has not been officially endorced
 * by Microsoft and therefore is not supported. The user of this code assumes any
 * and all risk with its use and should proceed with discression.
 * 
 * THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 */

using System;

using System.Runtime.InteropServices;

#region "Windows Data Types"
// http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx
using BOOL = System.Int32;     // typedef int BOOL;
using WORD = System.UInt16;    // typedef unsigned short WORD;
using DWORD = System.UInt32;   // typedef unsigned long DWORD;
using SHORT = System.Int16;    // typedef short SHORT;
using USHORT = System.UInt16;  // typedef unsigned short USHORT;
using INT = System.Int32;      // typedef int INT;
using UINT = System.UInt32;    // typedef unsigned int UINT;
using LONG = System.Int32;     // typedef long LONG;
using ULONG = System.UInt32;   // typedef unsigned long ULONG;
using FLOAT = System.Single;   // typedef float FLOAT;
using LPCWSTR = System.String; // typedef CONST WCHAR *LPCWSTR; 
using LPWSTR = System.String;  // typedef WCHAR *LPWSTR;
using LCID = System.UInt32;    // typedef DWORD LCID;

using CLSID = System.Guid;
using VARTYPE = System.UInt16;
using PROPID = System.UInt32;
using DOUBLE = System.Double;
#endregion

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct PROPVARIANT_URL_VECTOR
{
    public PROPVARIANT Url;
    public PROPVARIANT LastModified;
}

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct CAPROPVARIANT 
{
    public ULONG cElems;
    public IntPtr pElems; // Pointer to a single PROPVARIANT or an array of PROPVARIANT structures.
}

[StructLayout(LayoutKind.Explicit, Pack=1)]
public struct PROPVARIANT_UNION
{
    [FieldOffset(0)] public IntPtr pwszVal;          // VT_LPWSTR
    [FieldOffset(0)] public SHORT iVal;              // VT_I2
    [FieldOffset(0)] public USHORT uiVal;            // VT_UI2
    [FieldOffset(0)] public INT intVal;              // VT_INT
    [FieldOffset(0)] public UINT uintVal;            // VT_UINT
    [FieldOffset(0)] public LONG lVal;               // VT_I4
    [FieldOffset(0)] public ULONG ulVal;             // VT_UI4
    [FieldOffset(0)] public DOUBLE dblVal;           // VT_R8
    [FieldOffset(0)] public FILETIME filetime;       // VT_FILETIME
    [FieldOffset(0)] public CAPROPVARIANT capropvar; // VT_VECTOR | VT_VARIANT
}

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct PROPVARIANT
{
    public VARTYPE vt;
	public WORD wReserved1;
	public WORD wReserved2;
	public WORD wReserved3;

    public PROPVARIANT_UNION union;

    public PROPVARIANT(VarEnum vtype)
    {
        vt = (VARTYPE)vtype;
	    wReserved1 = 0;
	    wReserved2 = 0;
	    wReserved3 = 0;
        union = new PROPVARIANT_UNION();
    }

    /// <summary>
    /// Catch-all constructor to handle most common data types.
    /// </summary>
    /// <param name="objVal"></param>
    public PROPVARIANT(object objVal) : this(VarEnum.VT_EMPTY)
    {
        if (objVal.GetType() == typeof(string))
        {
            vt = (VARTYPE)VarEnum.VT_LPWSTR;
            union.pwszVal = Marshal.StringToCoTaskMemUni((string)objVal);
        }
        else if (objVal.GetType() == typeof(SHORT))
        {
            vt = (VARTYPE)VarEnum.VT_I2;
            union.iVal = (SHORT)objVal;
        }
        else if (objVal.GetType() == typeof(USHORT))
        {
            vt = (VARTYPE)VarEnum.VT_UI2;
            union.uiVal = (USHORT)objVal;
        }
        else if (objVal.GetType() == typeof(INT))
        {
            vt = (VARTYPE)VarEnum.VT_INT;
            union.intVal = (INT)objVal;
        }
        else if (objVal.GetType() == typeof(UINT))
        {
            vt = (VARTYPE)VarEnum.VT_UINT;
            union.uintVal = (UINT)objVal;
        }
        else if (objVal.GetType() == typeof(LONG))
        {
            vt = (VARTYPE)VarEnum.VT_I4;
            union.lVal = (LONG)objVal;
        }
        else if (objVal.GetType() == typeof(ULONG))
        {
            vt = (VARTYPE)VarEnum.VT_UI4;
            union.ulVal = (ULONG)objVal;
        }
        else if (objVal.GetType() == typeof(DOUBLE))
        {
            vt = (VARTYPE)VarEnum.VT_R8;
            union.dblVal = (DOUBLE)objVal;
        }
        else if (objVal.GetType() == typeof(FILETIME))
        {
            vt = (VARTYPE)VarEnum.VT_FILETIME;
            union.filetime = (FILETIME)objVal;
        }
        else if (objVal.GetType() == typeof(CAPROPVARIANT))
        {
            vt = (VARTYPE)(VarEnum.VT_VECTOR | VarEnum.VT_VARIANT);
            union.capropvar = (CAPROPVARIANT)objVal;
        }
        else
            throw new Exception("Unhandled PROPVARIANT type: " + objVal.GetType().ToString());
    }
  
    public PROPVARIANT(string pwszVal) : this(VarEnum.VT_LPWSTR)
    {
        union.pwszVal = Marshal.StringToCoTaskMemUni((string)pwszVal);
    }

    public PROPVARIANT(FILETIME filetime) : this(VarEnum.VT_FILETIME)
    {
        union.filetime = filetime;
    }

    public PROPVARIANT(PROPVARIANT_URL_VECTOR uv) : this(VarEnum.VT_VECTOR | VarEnum.VT_VARIANT)
    {
        union.capropvar.cElems = 2;
        union.capropvar.pElems = Marshal.AllocCoTaskMem(Marshal.SizeOf(uv));

        Marshal.StructureToPtr(uv, union.capropvar.pElems, false);
    }
}


[ComImport]
[Guid("0b63e318-9ccc-11d0-bcdb-00805fccce04")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUrlAccessor
{
	[PreserveSig]
    HRESULT AddRequestParameter(
        ref PROPSPEC pSpec, 
        ref PROPVARIANT pVar);

    [PreserveSig]
    HRESULT GetDocFormat(
        System.IntPtr wszDocFormat, 
        uint dwSize, 
        out uint pdwLength);
	
    [PreserveSig]
    HRESULT GetCLSID(
        out Guid pClsid);

   	[PreserveSig]
    HRESULT GetHost(
        System.IntPtr wszHost, 
        uint dwSize, 
        out uint pdwLength);

	[PreserveSig]
    HRESULT IsDirectory();

    [PreserveSig]
    HRESULT GetSize(
        out ulong pllSize);

    [PreserveSig]
    HRESULT GetLastModified(
        [MarshalAs(UnmanagedType.Struct)] 
        out FILETIME pftLastModified);

    [PreserveSig]
    HRESULT GetFileName(
        System.IntPtr wszFileName, 
        uint dwSize, 
        out uint pdwLength);

    [PreserveSig]
    HRESULT GetSecurityDescriptor(
        System.IntPtr pSD, 
        uint dwSize, 
        out uint pdwLength);

    [PreserveSig]
    HRESULT GetRedirectedURL(
        System.IntPtr wszRedirectedUrl, 
        uint dwSize, 
        out uint pdwLength);

    [PreserveSig]
    HRESULT GetSecurityProvider(
        out Guid pSPClsid);

    [PreserveSig]
    HRESULT BindToStream(
        out System.Runtime.InteropServices.ComTypes.IStream ppStream);

    [PreserveSig]
    HRESULT BindToFilter(
        out IFilter ppFilter);
}
